import Joi from 'joi';
import { nanoid } from 'nanoid';
import isIP from '../isIP';
import ipLimit from '../ipLimit';
import {
	TASK_TYPES,
	JOB_TASK_TYPES,
	AVILIABLE_TASK_MAP,
	TASK_CATEGORY_TYPES_MAP,
	CONTENT_KEYWORD_TYPES,
	CONTENT_PRIVACY_DISCLOSURE_TYPES,
} from '../constants';
import {
	sourceIp,
	securityStatus,
	taskSettings,
	triggerType,
	alertSettings,
	warnings,
	alertState,
} from './common';

const tld = (() => {
	let fn = null;
	import('tldjs').then(x => {
		fn = x.default;
	});
	return new Proxy(
		{},
		{
			get(val, prop) {
				if (!fn) return x => x;
				return fn[prop];
			},
		}
	);
})();

// #region result
export const resultNumbers = Joi.object({
	uid: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'User' } }),
	jobId: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'Job' } }),
	taskName: Joi.string().allow(null).description('任务名称'),
	target: Joi.string().required().description('监测目标'),
	datetime: Joi.date().default(Date.now).description('完成时间'),
	taskType: Joi.string()
		.meta({
			_mongoose: {
				enum: TASK_TYPES,
			},
		})
		.description('任务类型')
		.required(),
	taskId: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'Task' } }),
	highWarnCount: Joi.number().default(0).description('高危警告数'),
	mediumWarnCount: Joi.number().default(0).description('中危警告数'),
	lowWarnCount: Joi.number().default(0).description('低危警告数'),
	warnCount: Joi.number().default(0).description('警告总数'),
	normalNodeCount: Joi.number().default(0).description('可用性任务正常节点数'),
	abnorNodeCount: Joi.number().default(0).description('可用性任务异常节点数'),
	totalNodeCount: Joi.number().default(0).description('可用性任务节点总数'),
	aviliable: Joi.array().items(
		Joi.object({
			area: Joi.string().description('节点地区'),
			respTime: Joi.number().description('响应时间'), // 暂时保留，否则请求数据会报错
			totalTime: Joi.number().description('响应时间'),
		})
	),
});
// #endregion result

// #region task
export const task = Joi.object({
	sourceIp,
	taskSettings,
	alertSettings,
	securityStatus,
	triggerType,
	name: Joi.string().allow(null).description('任务名称'),
	uid: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', ref: 'User' } })
		.description('用户 ID'),
	taskId: Joi.string().allow(null, ''),
	taskSessionId: Joi.string().allow(null, ''),
	taskIdx: Joi.string().allow(null).description('任务序号'),
	crontabId: Joi.string().allow(null).description('后端定时任务 ID'),
	prevTaskId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Task' },
		})
		.allow(null)
		.description('上一次执行成功的任务'),
	refType: Joi.string().meta({ _mongoose: { enum: ['job', 'asset'] } }),
	refId: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', index: true } })
		.description('关联任务配置记录'),
	jobId: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', index: true, ref: 'Job' } })
		.description('关联Job'),
	isSpecialTask: Joi.boolean().default(false).description('是否是专项扫描'),
	domain: Joi.string().required().description('guan'),
	target: Joi.string().required().description('监测目标'),
	taskType: Joi.string()
		.meta({
			_mongoose: {
				enum: TASK_TYPES,
				index: true,
			},
		})
		.description('任务类型')
		.required(),

	startTime: Joi.date()
		.allow(null)
		.meta({ _mongoose: { index: true, default: Date.now } })
		.description('开始时间'),
	endTime: Joi.date()
		.allow(null)
		.meta({ _mongoose: { index: true } })
		.description('结束时间'),
	nextExecDate: Joi.date().allow(null).description('定时任务下次执行时间'),
	status: Joi.string()
		.meta({
			_mongoose: {
				index: true,
				enum: [
					'waiting', // 等待执行
					'active', // 正在执行
					'stalled', // 卡住， 这个可能需要人工干预
					'completed', // 完成
					'failed', // 失败
					'stopped', // 手动终止
				],
			},
		})
		.default('waiting')
		.description('任务执行状态'),
	progress: Joi.number().default(0).description('执行进度'),
	// 报错的错误消息
	exception: Joi.string().allow(null).description('任务错误信息'),
	repeat: Joi.object({
		cron: Joi.string().allow(null),
		tz: Joi.string().allow(null),
		startTime: Joi.date().allow(null),
		endTime: Joi.date().allow(null),
		limit: Joi.number().allow(null),
		every: Joi.number().allow(null),
		count: Joi.number().allow(null),
	})
		.allow(null)
		.description('定时设置'),
	result: Joi.object({
		warnings,
		warningCount: Joi.number().default(0).description('异常数量'),
		warningResolvedCount: Joi.number().default(0).description('已确认异常数量'),
		warningUnresolvedCount: Joi.number()
			.default(0)
			.description('未确认异常数量'),
		addition: Joi.object().unknown().allow(null),

		/**
		 * 网站画像
		 */
		isChanged: Joi.bool()
			.default(false)
			.allow(null)
			.description('网站画像是否有变动'),

		stats: Joi.object({
			/**
			 * 资产
			 */
			subdomainCount: Joi.number().empty(null, '').description('子域名数'),
			ipCount: Joi.number().default(0).empty(null, '').description('ip数'),
			portCount: Joi.number().empty(null, '').description('端口数'),
			ipBindDomainCount: Joi.number().empty(null, '').description('绑定域名数'),
			addSubdomainCount: Joi.number()
				.empty(null, '')
				.description('新增子域名数'),
			addIpCount: Joi.number().empty(null, '').description('新增ip数'),
			addPortCount: Joi.number().empty(null, '').description('新增端口数'),
			addIpBindDomainCount: Joi.number()
				.empty(null, '')
				.description('新增绑定域名数'),
			removeSubdomainCount: Joi.number()
				.empty(null, '')
				.description('移除子域名数'),
			removeIpCount: Joi.number().empty(null, '').description('移除ip数'),
			removePortCount: Joi.number().empty(null, '').description('移除端口数'),
			removeIpBindDomainCount: Joi.number()
				.empty(null, '')
				.description('移除绑定域名数'),
			changePortServiceCount: Joi.number()
				.empty(null, '')
				.description('端口服务改变数'),
			/**
			 * 内容合规
			 */
			// #region content
			url: Joi.number().empty(null, '').description('风险URL数'),
			keyword: Joi.number().empty(null, '').description('敏感词URL数'),
			privacy_disclosure: Joi.number()
				.empty(null, '')
				.description('隐私信息URL数'),
			contentChange: Joi.number().empty(null, '').description('变更URL数'),
			...CONTENT_PRIVACY_DISCLOSURE_TYPES.reduce(
				(obj, { idx, label }) => (
					(obj[`privacy_disclosure${idx}`] = Joi.number()
						.empty(null, '')
						.description(`隐私信息${label}`)), // eslint-disable-line no-sequences
					obj
				),
				{}
			),
			...CONTENT_KEYWORD_TYPES.reduce(
				(obj, { idx, label }) => (
					(obj[`keyword${idx}`] = Joi.number()
						.empty(null, '')
						.description(`敏感词${label}`)), // eslint-disable-line no-sequences
					obj
				),
				{}
			),
			// #endregion content
		}),
		ipPorts: Joi.array()
			.items(
				Joi.object({
					ip: Joi.string().empty(null).allow(''),
					hostname: Joi.string().allow(null).description('主机名'),
					location: Joi.string().allow(null).description('地理位置'),
					ports: Joi.array().items(
						Joi.object({
							service: Joi.string().allow(null),
							proto: Joi.string().empty(null),
							server: Joi.string().allow(null, ''),
							raw: Joi.string().allow(''),
							reason: Joi.string(),
							version: Joi.string().allow(null, ''),
							port: Joi.number(),
						})
					),
				})
			)
			.default([])
			.allow(null),
		subDomains: Joi.array()
			.items(
				Joi.object({
					host: Joi.string(),
					point: Joi.string().allow(null, ''),
				})
			)
			.default([])
			.allow(null),
		ipBindDomains: Joi.array()
			.items(
				Joi.object({
					ip: Joi.string().required(),
					domains: Joi.array().items(
						Joi.object({
							domain: Joi.string(),
							title: Joi.string().allow(''),
						})
					),
				})
			)
			.allow(null)
			.default([])
			.description('源 IP 绑定的其他域名'),
	}).description('扫描结果'),
	isLatestTask: Joi.boolean()
		.default(false)
		.description('最后一次执行的监测任务'),
	isSendAlert: Joi.boolean().default(false).description('是否发送告警'),
});

export const taskWithId = task.append({
	_id: Joi.string(),
});

// 用于保存后端回调的任务信息的保存
export const taskv2 = task;

// #endregion task

// #region job

/**
 * NOTE:
 * http 任务可以创建多个，所以 http 任务对应的字段存为数组
 * 查找时以settings中的顺序作为对应
 */
export const job = Joi.object({
	assetType: Joi.string()
		.allow(null, '')
		.meta({ _mongoose: { index: true } })
		.description('资产类型'),
	sourceIp,
	alertSettings,
	defense_target_id: Joi.any().meta({
		_mongoose: { index: true },
	}),
	verification: Joi.object({
		isVerified: Joi.boolean()
			.default(false)
			.description('是否通过网站所有权验证'),
		challenge: Joi.string().default(nanoid).description('验证Key'),
		verifyType: Joi.string()
			.allow(null)
			.meta({
				_mongoose: { enum: ['yunaq', 'embedHtml', 'file', 'oms'] },
			})
			.description('验证的方法'),
		verifiedTime: Joi.date().allow(null).description('通过验证的时间'),
		note: Joi.string().allow('', null).description('备注'),
	}).description('网站所有权验证'),
	targetUrl: Joi.string().description('目标地址'),
	target: Joi.object({
		// http: or https:
		protocol: Joi.string().valid('http:', 'https:').description('协议'),
		host: Joi.string()
			.case('lower')
			.trim()
			.meta({ _mongoose: { index: true } })
			.description('主机, 主机名:端口号'),
		hostname: Joi.string().case('lower').trim().description('主机名'),
		port: Joi.number()
			.allow(null)
			.meta({ _mongoose: { index: true } })
			.description('端口号'),
		ipType: Joi.number()
			.meta({
				_mongoose: { enum: [0, 4, 6] },
			})
			.valid(0, 4, 6)
			.default(0)
			.description('hostname的IP类型'),
		eTLD1: Joi.string()
			.allow(null)
			.case('lower')
			.trim()
			.description('从主机名提取的 eTLD+1 域名'),
	}).custom((value, helpers) => {
		let ret = value;

		try {
			const { host = '', hostname = '', protocol = '', port } = new URL(
				`${value.protocol}//${value.host || ''}`
			);
			ret = {
				host,
				hostname,
				protocol,
				port: port ? parseInt(port, 10) : protocol === 'http:' ? 80 : 443,
				eTLD1: tld.getDomain(hostname),
				ipType: isIP(hostname),
			};
		} catch {}

		try {
			const url = new URL(`${value.protocol}//${value.host}`);
			if (!url) throw new Error();
		} catch (error) {
			return helpers.message('无效主机名');
		}

		const hostname = ret.hostname;
		if (isIP(hostname)) {
			if (isIP(hostname) === 0 || ipLimit(hostname)) {
				return helpers.message('无效地址');
			}
		} else {
			const domain = tld.parse(hostname);
			if (!domain.domain || !domain.tldExists) {
				return helpers.message('无效域名');
			}
		}
		return ret;
	}, '资产地址无效'),
	note: Joi.string()
		.trim()
		.max(50)
		.messages({
			'string.empty': '资产名称不能为空',
			'string.max': '资产名称不能超过50个字符',
		})
		.required(),
	uid: Joi.string().meta({
		_mongoose: { type: 'ObjectId', ref: 'User', index: true },
	}),
	enableMonitor: Joi.boolean().default(false).description('启用监测'),
	enableAlert: Joi.boolean().default(false).description('启用告警'),
	cydEnabled: Joi.boolean()
		.allow(null)
		.default(null)
		.description('当前域名是否使用创宇盾'),
	ignoredWarnIdMap: Joi.any()
		.default({})
		.description('忽略的异常ID, { [warnId]: Date }'),
	assetId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Asset' },
		})
		.description('关联资产ID'),
	sitePortraitId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'SitePortrait' },
		})
		.allow(null)
		.description('关联网站画像任务ID'),
	createTime: Joi.date().default(Date.now).description('创建时间'),
	prelogin: Joi.object({
		site_login: Joi.boolean().default(false).description('网页登录'),
		login_method: Joi.string().default('form').description('网页登录选项'),
		login_form_action_url: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('表单登录url'),
		login_form_postdata: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('表单登录post'),
		login_json_action_url: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('json登录url'),
		login_json_postdata: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('json登录post'),
		request_cookie: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('cookie登录'),
		request_http_headers: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('headers登录'),
		is_http_auth_enabled: Joi.boolean()
			.default(false)
			.description('是否http认证'),
		http_auth_username: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('http认证用户名'),
		http_auth_password: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('http认证密码'),
		is_proxy_enabled: Joi.boolean()
			.default(false)
			.description('是否代理服务器'),
		proxy_scheme: Joi.string()
			.allow('')
			.default('http')
			.trim()
			.description('代理服务器协议'),
		proxy_host: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('代理服务器IP地址'),
		proxy_port: Joi.any().allow(null).default(80).description('代理服务器端口'),
		proxy_username: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('代理服务器用户名'),
		proxy_password: Joi.string()
			.allow('')
			.default('')
			.trim()
			.description('代理服务器密码'),
		is_proxy_need_auth: Joi.boolean().default(true),
	}),
	customMonitorPage: Joi.object({
		includeUrl: Joi.array()
			.items(Joi.string().trim())
			.max(100)
			.description('监测包含URL')
			.messages({
				'array.max': '该功能最多添加100个参数',
			}),
		excludeUrl: Joi.array()
			.items(Joi.string().trim())
			.max(100)
			.description('监测不包含URL')
			.messages({
				'array.max': '该功能最多添加100个参数',
			}),
	}),
	// 任务设置
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}Settings`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(taskSettings).default([])
			: taskSettings;
		return obj;
	}, {}),
	//  当前任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}StatusId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}IsStarted`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(Joi.boolean().default(false)).default([])
			: Joi.boolean().default(false).description('是否正在执行');
		return obj;
	}, {}),
	// 任务状态
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = securityStatus;
		obj[`${type}SecurityStatus`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema;
		return obj;
	}, {}),
	// 上一次执行成功的任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}PrevResultId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	// 执行成功的任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}ResultId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	// 是否异常
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.boolean().allow(null).description('是否异常');
		obj[`${type}Abnormal`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema;
		return obj;
	}, {}),
	// 告警发送状态/计数
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}AlertState`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(alertState).default([])
			: alertState.allow(null);
		return obj;
	}, {}),
	// 周期任务下次执行时间
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}NextExecDate`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(Joi.date().allow(null)).default([])
			: Joi.date().allow(null);
		return obj;
	}, {}),
});
export const job2 = Joi.object({
	sourceIp,
	alertSettings,
	verification: Joi.object({
		isVerified: Joi.boolean()
			.default(false)
			.description('是否通过网站所有权验证'),
		challenge: Joi.string().default(nanoid).description('验证Key'),
		verifyType: Joi.string()
			.allow(null)
			.meta({
				_mongoose: { enum: ['yunaq', 'embedHtml', 'file', 'oms'] },
			})
			.description('验证的方法'),
		verifiedTime: Joi.date().allow(null).description('通过验证的时间'),
		note: Joi.string().allow('', null).description('备注'),
	}).description('网站所有权验证'),
	targetUrl: Joi.string().description('目标地址'),
	target: Joi.object().custom((value, helpers) => {
		let ret = value;

		try {
			const { host = '', hostname = '', protocol = '', port } = new URL(
				`${value.protocol}//${value.host || ''}`
			);
			ret = {
				host,
				hostname,
				protocol,
				port: port ? parseInt(port, 10) : protocol === 'http:' ? 80 : 443,
				eTLD1: tld.getDomain(hostname),
				ipType: isIP(hostname),
			};
		} catch {
			console.log('job2 err');
		}

		try {
			const url = new URL(`${value.protocol}//${value.host}`);
			if (!url) throw new Error();
		} catch (error) {
			return helpers.message('无效主机名');
		}

		const hostname = ret.hostname;
		if (isIP(hostname)) {
			if (isIP(hostname) === 0 || ipLimit(hostname)) {
				return helpers.message('无效地址');
			}
		} else {
			const domain = tld.parse(hostname);
			if (!domain.domain || !domain.tldExists) {
				return helpers.message('无效域名');
			}
		}
		return ret;
	}, '资产地址无效'),
	note: Joi.string()
		.trim()
		.max(50)
		.messages({
			'string.empty': '资产名称不能为空',
			'string.max': '资产名称不能超过50个字符',
		})
		.required(),
	uid: Joi.string().meta({
		_mongoose: { type: 'ObjectId', ref: 'User', index: true },
	}),
	enableMonitor: Joi.boolean().default(false).description('启用监测'),
	enableAlert: Joi.boolean().default(false).description('启用告警'),
	cydEnabled: Joi.boolean()
		.allow(null)
		.default(null)
		.description('当前域名是否使用创宇盾'),
	ignoredWarnIdMap: Joi.any()
		.default({})
		.description('忽略的异常ID, { [warnId]: Date }'),
	assetId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Asset' },
		})
		.description('关联资产ID'),
	sitePortraitId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'SitePortrait' },
		})
		.allow(null)
		.description('关联网站画像任务ID'),
	createTime: Joi.date().default(Date.now).description('创建时间'),
	cookie: Joi.string().trim().allow('', null).description('自定义Cookie'),
	customMonitorPage: Joi.object({
		includeUrl: Joi.array()
			.items(Joi.string().trim())
			.max(100)
			.description('监测包含URL')
			.messages({
				'array.max': '该功能最多添加100个参数',
			}),
		excludeUrl: Joi.array()
			.items(Joi.string().trim())
			.max(100)
			.description('监测不包含URL')
			.messages({
				'array.max': '该功能最多添加100个参数',
			}),
	}),
	// 任务设置
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}Settings`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(taskSettings).default([])
			: taskSettings;
		return obj;
	}, {}),
	//  当前任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}StatusId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}IsStarted`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(Joi.boolean().default(false)).default([])
			: Joi.boolean().default(false).description('是否正在执行');
		return obj;
	}, {}),
	// 任务状态
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = securityStatus;
		obj[`${type}SecurityStatus`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema;
		return obj;
	}, {}),
	// 上一次执行成功的任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}PrevResultId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	// 执行成功的任务ID
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.string()
			.meta({
				_mongoose: { type: 'ObjectId', ref: 'Task' },
			})
			.allow(null);
		obj[`${type}ResultId`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema.allow(null);
		return obj;
	}, {}),
	// 是否异常
	...JOB_TASK_TYPES.reduce((obj, type) => {
		const schema = Joi.boolean().allow(null).description('是否异常');
		obj[`${type}Abnormal`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(schema).default([])
			: schema;
		return obj;
	}, {}),
	// 告警发送状态/计数
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}AlertState`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(alertState).default([])
			: alertState.allow(null);
		return obj;
	}, {}),
	// 周期任务下次执行时间
	...JOB_TASK_TYPES.reduce((obj, type) => {
		obj[`${type}NextExecDate`] = AVILIABLE_TASK_MAP[type]
			? Joi.array().items(Joi.date().allow(null)).default([])
			: Joi.date().allow(null);
		return obj;
	}, {}),
});
export const jobWithId = job.append({
	_id: Joi.string(),
});
// #endregion job

// #region asset
export const asset = Joi.object({
	taskSettings,
	triggerType,
	securityStatus,
	alertState,
	alertSettings,
	isStarted: Joi.boolean().default(false).description('是否正在执行'),
	target: Joi.string().required().description('资产'),
	targetType: Joi.string().description('资产类型。域名/IP'),
	uid: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'User' },
		})
		.description('用户ID'),
	createTime: Joi.date().default(Date.now).description('创建时间'),
	updateTime: Joi.date().allow(null).description('更新时间'),
	nextExecDate: Joi.date().allow(null).description('下次执行时间'),
	jobId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Job' },
		})
		.description('资产ID'),
	taskId: Joi.string().description('调度任务ID'),
	taskSessionId: Joi.string().description('调度任务Session ID'),
	resultId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Task' },
		})
		.description('任务结果ID'),
	prevResultId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Task' },
		})
		.description('上一执行成功的任务ID'),
	prevTaskId: Joi.string()
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Task' },
		})
		.description('上一执行的任务ID'),
	customSubdomains: Joi.array()
		.items(
			Joi.object({
				domain: Joi.string().description('域名'),
				ip: Joi.string().allow(null).description('IP'),
				note: Joi.string().allow(null).description('备注'),
			})
		)
		.description('自定义添加子域名'),
	ignoredWarnIdMap: Joi.any()
		.default({})
		.description('忽略的异常ID, { [warnId]: Date }'),
	yunaqBlacklist: Joi.object({
		enable: Joi.bool().default(false).description('启用'),
		vul: Joi.object({
			high: Joi.number().default(0).description('高'),
			medium: Joi.number().default(0).description('中'),
			low: Joi.number().default(0).description('低'),
		})
			.default({})
			.description('安全漏洞'),
		securityEvent: Joi.object({
			black_links: Joi.number().default(0).description('暗链'),
			broken_links: Joi.number().default(0).description('坏链'),
			cryjack: Joi.bool().default(false).description('挖矿'),
			malscan: Joi.bool().default(false).description('挂马'),
		})
			.allow({})
			.description('安全事件'),
	}).description('云防联动加黑'),
});

export const assetWithId = asset.append({
	_id: Joi.string(),
});
// #endregion asset

// #region alert
export const alert = Joi.object({
	securityStatus,
	domain: Joi.string().description('监测任务域名'),
	refId: Joi.string().meta({ _mongoose: { type: 'ObjectId' } }),
	jobId: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'Job' } }),
	taskId: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', ref: 'Task' } })
		.description('任务ID'),
	taskName: Joi.string().allow('', null).description('任务名称'),
	taskCategory: Joi.string()
		.meta({ _mongoose: { index: true } })
		.description('任务组类型'),
	taskType: Joi.string()
		.meta({
			_mongoose: { enum: TASK_TYPES },
		})
		.description('任务类型'),
	taskTriggerType: triggerType,
	target: Joi.string().description('监测目标'),
	alertType: Joi.string()
		.meta({
			_mongoose: { enum: ['normal', 'abnormal'] },
		})
		.description('告警信息类型。正常/异常'),
	title: Joi.string().description('告警标题'),
	summary: Joi.string().allow('').description('告警摘要'),
	content: Joi.string().allow('').description('告警内容'),
	detail: Joi.object().unknown().allow(null).description('附加信息'),
	uid: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'User' } }),
	createTime: Joi.date().default(Date.now).description('发布时间'),
	duration: Joi.number().default(0).description('持续时间，毫秒'),
	isResolved: Joi.boolean().default(false).description('告警是否标记为已修复'),
});

export const alertWithId = alert.append({
	_id: Joi.string(),
});
// #endregion alert

// #region notificationSendLog
export const notificationSendLog = Joi.object({
	refId: Joi.string().meta({ _mongoose: { type: 'ObjectId' } }),
	taskId: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'Task' } }),
	domain: Joi.string().description('监测任务域名'),
	status: Joi.string()
		.default('pending')
		.meta({ _mongoose: { enum: ['pending', 'success', 'fail'] } })
		.description('推送状态'),
	type: Joi.string()
		.meta({
			_mongoose: {
				enum: Object.keys(TASK_CATEGORY_TYPES_MAP).concat([
					'planWillExpired', // 套餐即将到期通知
					'planExpired', // 套餐到期通知
					'weeklyReport', // 周报
					'monthlyReport', // 月报
					'abnormalLoginLocation', // 异地登录提醒
					'monitorException', // 监测异常通知
				]),
			},
		})
		.description('任务组类型'),
	taskCategory: Joi.string()
		.meta({ _mongoose: { index: true } })
		.description('任务组类型'),
	taskType: Joi.string()
		.meta({
			_mongoose: { enum: TASK_TYPES },
		})
		.description('任务类型'),
	target: Joi.string().description('监测目标'),
	uid: Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'User' } }),
	startTime: Joi.date().default(Date.now).description('发送时间'),
	endTime: Joi.date().description('完成时间'),
	title: Joi.string().description('标题'),
	summary: Joi.string().allow(null, '').description('摘要'),
	content: Joi.string().description('内容'),
	receivers: Joi.array()
		.items(
			Joi.object({
				type: Joi.string()
					.meta({ _mongoose: { enum: ['wechat', 'email', 'phone'] } })
					.description('邮箱/手机/微信'),
				contact: Joi.string().description('联系地址'),
				status: Joi.string().allow(null).description('发送状态'),
				statusCode: Joi.string().allow(null).description('发送状态码'),
			})
		)
		.description('接收列表'),
});

export const notificationSendLogWithId = notificationSendLog.append({
	_id: Joi.string().allow(''),
});
// #endregion notificationSendLog

// #region alertGroup
export const alertGroup = Joi.object({
	uid: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', ref: 'User' } })
		.description('所属用户ID'),
	name: Joi.string().required().description('名称'),
	includeWechat: Joi.bool().default(false).description('发送微信告警'),
	receiverIds: Joi.array()
		.items(
			Joi.string().meta({
				_mongoose: { type: 'ObjectId', ref: 'AlertReceiver' },
			})
		)
		.required()
		.description('告警人 ID 列表'),
	isDefault: Joi.boolean().default(false).description('是否是默认组'),
	enable: Joi.boolean().default(false).description('是否启用'),
});

export const alertGroupWithId = alertGroup.append({
	_id: Joi.string(),
});
// #endregion

// #region alertReceiver
export const alertReceiver = Joi.object({
	uid: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', ref: 'User' } })
		.description('所属用户ID'),
	name: Joi.string()
		.required()
		.messages({
			'any.required': '请输入名称',
			'string.empty': '请输入名称',
		})
		.description('名称'),
	phone: Joi.string().allow('', null).description('电话'),
	phoneVerified: Joi.boolean().default(false).description('电话验证状态'),
	email: Joi.string()
		.email({ tlds: { allow: false } })
		.message('邮箱格式不正确')
		.allow('', null)
		.description('邮箱'),
	emailVerified: Joi.boolean().default(false).description('邮箱验证状态'),
	enable: Joi.boolean().default(true).description('是否启用'),
	wechat: Joi.string().allow(null, '').description('微信'),
	isDefault: Joi.boolean().default(false).description('是否是默认告警人'),
});

export const alertReceiverWithId = alertReceiver.append({
	_id: Joi.string(),
});
// #endregion

// #region news
export const news = Joi.object({
	title: Joi.string().required().description('标题'),
	author: Joi.string().allow(null, '').description('作者'),
	type: Joi.string().required().description('类型'),
	link: Joi.string().allow(null, '').description('外链地址'),
	summary: Joi.string().allow('').description('摘要'),
	content: Joi.string().allow(null, '').description('正文'),
	isSendNotification: Joi.boolean().default(false).description('站内信显示'),
	isConsoleDisplay: Joi.boolean().default(false).description('控制台主页显示'),
	isHomepageDisplay: Joi.boolean().default(false).description('首页显示'),
	isRecommendDisplay: Joi.boolean().default(false).description('推荐栏显示'),
	isDraft: Joi.boolean().default(true).description('是否是草稿'),
	image: Joi.string().allow(null, '').description('封面图片'),
	imgName: Joi.string().allow(null, '').description('图片名称'),
	datetime: Joi.date().default(Date.now).description('时间'),
}).label('News');

export const newsWithId = news.append({
	_id: Joi.string(),
});
// #endregion

// #region whitelist
export const whitelist = Joi.object({
	uid: Joi.string()
		.meta({ _mongoose: { type: 'ObjectId', ref: 'User' } })
		.description('所属用户ID'),
	jobIds: Joi.array()
		.items(Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'User' } }))
		.description('资产ID'),
	validStartTime: Joi.date().allow(null).description('生效时间大于等于'),
	validEndTime: Joi.date().allow(null).description('生效时间小于等于'),
	urls: Joi.array().items(Joi.string()).description('URL列表'),
	taskTypes: Joi.array().items(Joi.string()).description('生效事件'),
	vulWarnIds: Joi.array().items(Joi.string()).description('加白漏洞ID'),
	normalEventWarnIds: Joi.array()
		.items(Joi.string())
		.description('加白基础事件ID'),
	attackEventWarnIds: Joi.array()
		.items(Joi.string())
		.description('加白攻击事件ID'),
	explicitKeywords: Joi.array().items(Joi.string()).description('加白漏洞ID'),
	note: Joi.string().allow('', null).description('备注'),
	enable: Joi.boolean().default(false).description('状态'),
	updateTime: Joi.date().default(Date.now).description('更新时间'),
	createTime: Joi.date().default(Date.now).description('时间'),
	modifiedBy: Joi.string().allow(null).description('后台操作用户'),
}).label('Whitelist');

export const whitelistWithId = whitelist.append({
	_id: Joi.string(),
});
// #endregion

// #region blacklist
export const blacklist = Joi.object({
	target: Joi.string().description('域名/URL/IP'),
	uids: Joi.array()
		.items(Joi.string().meta({ _mongoose: { type: 'ObjectId', ref: 'User' } }))
		.description('所属用户ID'),
	taskCategories: Joi.array().items(Joi.string()).description('生效事件'),
	note: Joi.string().description('备注'),
	enable: Joi.boolean().default(false).description('状态'),
	createTime: Joi.date().default(Date.now).description('时间'),
	updateTime: Joi.date().default(Date.now).description('更新时间'),
	createBy: Joi.string()
		.allow(null)
		.meta({ _mongoose: { type: 'ObjectId', ref: 'User' } })
		.description('创建用户'),
}).label('Blacklist');

export const blacklistWithId = blacklist.append({
	_id: Joi.string(),
});
// #endregion

// #region sitePortrait
export const sitePortrait = Joi.object({
	triggerType,
	taskSettings,
	securityStatus,
	alertSettings,
	uid: Joi.string().meta({
		_mongoose: { type: 'ObjectId', ref: 'User' },
	}),
	jobId: Joi.string().meta({
		_mongoose: { type: 'ObjectId', ref: 'Job' },
	}),
	target: Joi.string().required(),
	isChanged: Joi.boolean().default(false),
	portrait: Joi.string().description('URL Tree').required(),
	lastChangeId: Joi.string()
		.allow('', null)
		.meta({
			_mongoose: { type: 'ObjectId', ref: 'Task' },
		}),
	createTime: Joi.date().default(Date.now).description('创建时间'),
	updateTime: Joi.date().allow(null).description('更新时间'),
	nextExecDate: Joi.date().allow(null).description('下次执行时间'),
	version: Joi.number().default(0).description('版本'),
}).label('SitePortrait');
export const sitePortraitWithId = sitePortrait.append({
	_id: Joi.string(),
});
// #endregion
